package minio

import (
	"context"
	"errors"
	"io"
	"net/url"
	"time"

	"github.com/minio/minio-go/v7"
)

var (
	Default            bucket
	errBucketNotExists error                     = errors.New("bucket不存在")
	statOption         minio.StatObjectOptions   = minio.StatObjectOptions{}
	deleteOption       minio.RemoveObjectOptions = minio.RemoveObjectOptions{ForceDelete: true}
	putOption          minio.PutObjectOptions    = minio.PutObjectOptions{}
	getOption          minio.GetObjectOptions    = minio.GetObjectOptions{}
)

type bucket struct{ name string }

func (b bucket) Exists() error {
	i, err := minioClient.BucketExists(context.Background(), b.name)
	if err != nil {
		return err
	} else if !i {
		return errBucketNotExists
	}
	return nil
}

// 列出指定路径下的对象
// @param: prefix 对象名称前缀
// @param: recursive 是否向下递归
func (b bucket) List(prefix string, recursive bool) []minio.ObjectInfo {
	opts := minio.ListObjectsOptions{Prefix: prefix, Recursive: recursive}
	var result []minio.ObjectInfo = []minio.ObjectInfo{}
	for obj := range minioClient.ListObjects(context.Background(), b.name, opts) {
		if err := obj.Err; err != nil {
			break
		}
		result = append(result, obj)
	}
	return result
}

// 获取对象的元数据
func (b bucket) Stat(objectName string) (minio.ObjectInfo, error) {
	return minioClient.StatObject(context.Background(), b.name, objectName, statOption)
}

// 删除一个对象
func (b bucket) Remove(objectName string) error {
	return minioClient.RemoveObject(context.Background(), b.name, objectName, deleteOption)
}

// 删除一个未完整上传的对象
func (b bucket) RemoveIncompleteUpload(objectName string) error {
	return minioClient.RemoveIncompleteUpload(context.Background(), b.name, objectName)
}

// 上传覆盖
// 当对象小于128MiB时，FPutObject直接在一次PUT请求里进行上传。
// 当大于128MiB时，根据文件的实际大小，FPutObject会自动地将对象进行拆分成128MiB一块或更大一些进行上传。
// 对象的最大大小是5TB。
func (b bucket) Put(localPath, objectName string) (info minio.UploadInfo, err error) {
	return minioClient.FPutObject(context.Background(), b.name, objectName, localPath, putOption)
}

// 流上传
func (b bucket) PutIO(destFullPath, contentType string, reader io.Reader) (info minio.UploadInfo, err error) {
	return minioClient.PutObject(context.Background(), b.name, destFullPath, reader, int64(-1), minio.PutObjectOptions{ContentType: contentType})
}

// 下载并将文件保存到本地文件系统
func (b bucket) Get(objectName, localPath string) error {
	return minioClient.FGetObject(context.Background(), b.name, objectName, localPath, getOption)
}

// 获取下载流
func (b bucket) GetIO(destFullPath string) (*minio.Object, error) {
	return minioClient.GetObject(context.Background(), minioConfig.Bucket, destFullPath, getOption)
}

// 生成一个用于HTTP GET操作的分享链。
// 浏览器/移动客户端可以在即使存储桶为私有的情况下也可以通过这个URL进行下载。
// 这个分享链可以有一个过期时间，默认是7天。
// 注：生成共享链接的host，由MINIO_ENDPOINT的host部分决定，即，如果是内网host，则生成的共享链接只能内网使用
func (b bucket) PresignedGet(objectName, filename string, expires time.Duration) (u *url.URL, err error) {
	reqParams := make(url.Values)
	reqParams.Set("response-content-disposition", "attachment; filename=\""+filename+"\"")
	return minioClient.PresignedGetObject(context.Background(), b.name, objectName, expires, reqParams)
}

// 生成上传分享链
func (b bucket) PresignedPut(objectName string, expires time.Duration) (u *url.URL, err error) {
	return minioClient.PresignedPutObject(context.Background(), b.name, objectName, expires)
}
